#ifndef EXPRESSIONTYPE
#define EXPRESSIONTYPE

#include <iostream>

/**
    @brief classes de valeurs

    les différentes classes de valeur auxquelles on a accès
*/
typedef enum
{
    CLASS_ERROR, /**< erreur ou classe non définie */
    PARAMETER, /**< paramètreformel  d'une fonction/procédure */
    VARIABLE, /**< variable locale d'une fonction/procédure */
    CONSTANTE /**< une valeur constante */
} SymbolClass;

inline std::ostream& operator<< ( std::ostream& out, SymbolClass vclass )
{
    switch ( vclass )
    {
    case CLASS_ERROR:
        out << "erreur";
        break;
    case PARAMETER:
        out << "parametre";
        break;
    case VARIABLE:
        out << "variable";
        break;
    case CONSTANTE:
        out << "constante";
        break;
    }
    return out;
}

/**
    @brief les types de base

    les différents types de base associés aux identificateurs ou aux expressions
*/
typedef enum
{
    TYPE_ERROR, /**< erreur ou type indéfini */
    INTEGER, /**< type entier */
    REAL, /**< type réel */
    BOOLEAN, /**< type booléen */
    VOID, /**< type vide */
    STRING /**< type chaine de caractère */
} BaseType;

inline std::ostream& operator<< ( std::ostream& out, BaseType vclass )
{
    switch ( vclass )
    {
    case TYPE_ERROR:
        out << "erreur";
        break;
    case INTEGER:
        out << "int";
        break;
    case REAL:
        out << "float";
        break;
    case BOOLEAN:
        out << "bool";
        break;
    case VOID:
        out << "void";
        break;
    case STRING:
        out << "string";
        break;
    }
    return out;
}

/**
    @brief modificateurs de type

    les différents modificateurs de type possibles
*/
typedef enum
{
    NONE, /**< aucun, ie : type de base */
    ARRAY, /**< tableau */
    POINTER /**< pointeur */
} TypeModifier;

inline std::ostream& operator<< ( std::ostream& out, TypeModifier vmod )
{
    switch ( vmod )
    {
    case NONE:
        out << "NONE";
        break;
    case ARRAY:
        out << "ARRAY";
        break;
    case POINTER:
        out << "POINTER";
        break;
    }
    return out;
}

/**
    @brief définition d'un type composé

    permet de décrire un type quelconque
*/
typedef struct
{
    BaseType _base; /**< le type de base */
    TypeModifier _modifier; /**< le modificateur à appliquer au type de base */
    unsigned int _size; /**< taille du tableau, si c'est un tableau */
} Type;

inline std::ostream& operator<< ( std::ostream& out, Type vtype )
{
    out << "(" << vtype._modifier << "," << vtype._base << "," << vtype._size << ")";
    return out;
}

/**
    @brief taille en mémoire d'un type composé

    renvoie la taille en mémoire en fonction des paramètres du type
    @param [in] type le type dont on veut la taille
    @return la taille en mémoire nécessaire pour ce type
*/
extern unsigned int getTypeSize ( const Type& type );

#endif
